﻿using Furion;
using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using YFurion.DataFilters;
using YFurion.Entities;
using YFurion.Entities.Extensions;
using YFurion.Session;

namespace YFurion.EntityFrameworkCore
{
    public abstract class YDbContext<TDbContext, TDbContextLocator> : AppDbContext<TDbContext, TDbContextLocator>, IModelBuilderFilter<TDbContextLocator>
        where TDbContext : DbContext
        where TDbContextLocator : class, IDbContextLocator
    {
        protected IYSession YSession => App.GetService<IYSession>();

        protected IDataFilter DataFilter => App.GetService<IDataFilter>();

        protected virtual bool IsSoftDeleteFilterEnabled => DataFilter.IsFilterEnabled(DataFilterConst.SoftDelete) == true;

        private static MethodInfo ConfigureGlobalFiltersMethodInfo = typeof(YDbContext<TDbContext, TDbContextLocator>).GetMethod(nameof(ConfigureGlobalFilters), BindingFlags.Instance | BindingFlags.NonPublic);

        public YDbContext(DbContextOptions<TDbContext> options) : base(options)
        {
            EnabledEntityStateTracked = App.Configuration.Get<bool>("IsTracking");
        }

        #region 全局查询筛选器方法
        public virtual void OnCreating(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            IMutableEntityType entityType = entityBuilder.Metadata;
            ConfigureGlobalFiltersMethodInfo
                .MakeGenericMethod(entityType.ClrType)
                .Invoke(this, new object[] { modelBuilder, entityType });
        }

        public virtual void OnCreated(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
        }

        private void ConfigureGlobalFilters<TEntity>(ModelBuilder modelBuilder, IMutableEntityType entityType)
            where TEntity : class
        {
            if (entityType.BaseType == null)
            {
                var filterExpression = CreateFilterExpression<TEntity>();
                if (filterExpression != null)
                {
                    modelBuilder.Entity<TEntity>().HasQueryFilter(filterExpression);
                }
            }
        }

        protected virtual Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
           where TEntity : class
        {
            Expression<Func<TEntity, bool>> expression = CreateSoftDeleteFilterExpression<TEntity>();
            return expression;
        }

        protected virtual Expression<Func<TEntity, bool>> CreateSoftDeleteFilterExpression<TEntity>()
        {
            if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
            {
                Expression<Func<TEntity, bool>> softDeleteFilter = e => !((ISoftDelete)e).IsDeleted || ((ISoftDelete)e).IsDeleted != IsSoftDeleteFilterEnabled;
                return softDeleteFilter;
            }
            return null;
        }
        #endregion

        #region 数据库操作拦截器方法
        protected override void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> result)
        {
            IEnumerable<EntityEntry> entries = eventData.Context.ChangeTracker.Entries()
    .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified || e.State == EntityState.Deleted);
            foreach (EntityEntry entry in entries)
            {
                object entity = entry.Entity;
                switch (entry.State)
                {
                    case EntityState.Added:
                        entity.SetCreationProperties(YSession.SubjectId);
                        break;
                    case EntityState.Modified:
                        if (!entity.IsSoftDelete())
                        {
                            entity.SetModificationProperties(YSession.SubjectId);
                        }
                        break;
                    case EntityState.Deleted:
                        break;
                }
            }
            SavingChangesEventOuter(eventData, result);
        }

        protected virtual void SavingChangesEventOuter(DbContextEventData eventData, InterceptionResult<int> result)
        {
        }
        #endregion
    }
}